package com.ddw.meedeeuh.shared;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import java.util.logging.Level;

import com.ddw.meedeeuh.Meedeeuh;
import com.ddw.meedeeuh.shared.io.PathUtils;
import com.ddw.meedeeuh.shared.movie.MovieSettings;
import com.ddw.meedeeuh.shared.movie.MovieSettingsFactory;

/**
 * Represents a factory class for parsing and serializing
 * meedeeuh's global settings
 * @author cmcnicholas
 *
 */
public abstract class GlobalSettingsFactory {

	private static File PROPERTIES_FILE = null;
	private static final String FILE_LOG_ENABLED_KEY = "global.logging.file.enabled";
	private static final String LOG_LEVEL_KEY = "global.logging.level";

	/**
	 * Loads the global settings from the users local directory into memory
	 * @return a global settings instance
	 */
	public static GlobalSettings load() {
		final File propertiesFile = getPropertiesFile();

		// If the global settings file doesn't exist create it
		if(propertiesFile.exists() == false) {
			copyResourceToFileSystem(propertiesFile);
		}

		// Read properties
		final Properties properties = PropertiesUtils.readPropertiesFile(propertiesFile);

		// Parse the properties
		return parse(properties);
	}

	/**
	 * Saves a global settings instance to the users local directory
	 * @param globalSettings the global settings to save
	 * @param movieSettings the movieSettings to save
	 */
	public static void save(GlobalSettings globalSettings, MovieSettings movieSettings) {
		final File propertiesFile = getPropertiesFile();

		// Serialize the properties
		final Properties properties = serialize(globalSettings);
		final Properties movieProperties = MovieSettingsFactory.serialize(movieSettings);
		properties.putAll(movieProperties);

		// Save the properties
		FileOutputStream os = null;
		try {
			os = new FileOutputStream(propertiesFile);
			properties.store(os, "Global application properties for Meedeuh");
		} catch (IOException e) {
			throw new RuntimeException("There was a problem saving the properties file", e);
		} finally {
			if(os != null) {
				try {
					os.close();
				} catch (IOException e) {
					throw new RuntimeException("Failed to close the properties file", e);
				}
			}
		}
	}

	/**
	 * Gets the properties file instance
	 * @return a file instance
	 */
	public static File getPropertiesFile() {
		if(PROPERTIES_FILE == null) {
			PROPERTIES_FILE = new File(PathUtils.getCurrentDirectory(), "Meedeeuh.properties");
		}
		return PROPERTIES_FILE;
	}

	/**
	 * Copies an internal resource properties file to the file system
	 * @param file the location to copy to
	 */
	private static void copyResourceToFileSystem(final File file) {
		final InputStream is = Meedeeuh.class.getResourceAsStream("Meedeeuh.properties");
		if(is == null) {
			throw new RuntimeException("Global properties file was not found");
		}

		OutputStream os = null;
		try {
			os = new FileOutputStream(file);
			byte[] buffer = new byte[2048];
			int length;
			while((length = is.read(buffer)) > 0) {
				os.write(buffer, 0, length);
			}
		} catch(Throwable e) {
			throw new RuntimeException("Failed to copy meedeeuh.properties to file system", e);
		} finally {
			if(os != null) {
				try {
					os.close();
				} catch (IOException e) {
					throw new RuntimeException("Failed to copy meedeeuh.properties to file system, cannot close output stream", e);
				}
			}
			try {
				is.close();
			} catch (IOException e) {
				throw new RuntimeException("Failed to copy meedeeuh.properties to file system, cannot close input stream", e);
			}
		}
	}

	/**
	 * Parses a global setting properties file
	 * @param properties the properties file to parse
	 * @return a global settings instance
	 */
	private static GlobalSettings parse(Properties properties) {
		if(properties == null) {
			throw new IllegalArgumentException("The properties parameter cannot be null");
		}

		final boolean fileLogEnabled = PropertiesUtils.parseBoolean(properties, FILE_LOG_ENABLED_KEY, false, true);
		final Level logLevel = Level.parse(PropertiesUtils.parseString(properties, LOG_LEVEL_KEY, "OFF", true));

		return new GlobalSettingsImpl(fileLogEnabled, logLevel);
	}

	/**
	 * Serializes a global settings instance into a properties file instance
	 * @param settings the settings to serialize
	 * @return a properties file instance
	 */
	private static Properties serialize(GlobalSettings settings) {
		if(settings == null) {
			throw new IllegalArgumentException("The settings parameter cannot be null");
		}

		final Properties properties = new Properties();
		properties.setProperty(FILE_LOG_ENABLED_KEY, PropertiesUtils.serializeBoolean(settings.isFileLogEnabled()));
		properties.setProperty(LOG_LEVEL_KEY, settings.getLogLevel().getName());
		return properties;
	}

}
